
package org.apache.osgimaker.analyse.algorithm.dependency;

import static org.apache.osgimaker.analyse.algorithm.dependency.DependencyDefinitionParser.DIRECTLY_INDEPENDENT_OF_KEY_WORD;

import java.util.HashSet;
import java.util.Set;

import org.apache.osgimaker.analyse.algorithm.graph.AtomicVertex;
import org.apache.osgimaker.analyse.algorithm.graph.PathsFinder;
import org.apache.osgimaker.analyse.algorithm.graph.Vertex;
import org.apache.osgimaker.analyse.algorithm.graph.VertexCondition;
import org.apache.osgimaker.analyse.algorithm.util.OrStringPattern;
import org.apache.osgimaker.analyse.algorithm.util.StringPattern;



public class DependencyStatement implements Statement
{
  private static final class VertexUnionCondition implements VertexCondition
  {
    private final VertexCondition[] _conditions;

    VertexUnionCondition(VertexCondition[] conditions)
    {
      _conditions = conditions;
    }

    public boolean isFulfilled(Vertex vertex)
    {
      for (VertexCondition condition : _conditions)
      {
        if (condition.isFulfilled(vertex))
        {
          return true;
        }
      }
      return false;
    }
  }
  
  private static final String CHECK = DependencyDefinitionParser.CHECK_KEY_WORD + ' ';
  private final StringPattern[] _startSets;
  private final StringPattern[] _finalSets;
  private final StringPattern _finalSet;
  private final String _dependencyType;
  private final VertexCondition[] _startConditions;
  private final VertexCondition[] _finalConditions;
  private final VertexCondition _finalCondition;
  private final SetDefinitionRepository _repository;
  private final ResultRenderer _renderer;
  
  public DependencyStatement(StringPattern[] startSets,
                             StringPattern[] finalSets,
                             String dependencyType,
                             SetDefinitionRepository repository,
                             ResultRenderer renderer)
  {
    _startSets = startSets;
    _finalSets = finalSets;
    _dependencyType = dependencyType;
    _repository = repository;
    _renderer = renderer;
    _startConditions = createVertexConditions(startSets);
    _finalConditions = createVertexConditions(finalSets);
    _finalSet = new OrStringPattern(_finalSets);
    _finalCondition = new VertexUnionCondition(_finalConditions);
  }
  
  private VertexCondition[] createVertexConditions(StringPattern[] patterns)
  {
    VertexCondition[] fromSets = new VertexCondition[patterns.length];
    for (int i = 0; i < fromSets.length; i++)
    {
      fromSets[i] = new PatternVertexCondition(patterns[i]);
    }
    return fromSets;
  }

  public Result execute(AtomicVertex[] graph)
  {
    ResultContainer result = new ResultContainer();
    boolean directPathsOnly = DIRECTLY_INDEPENDENT_OF_KEY_WORD.equals(_dependencyType);
    boolean dependsOnly = DependencyDefinitionParser.DEPENDENT_ONLY_ON_KEY_WORD.equals(_dependencyType);
    for (int i = 0; i < _startConditions.length; i++)
    {
      VertexCondition startCondition = _startConditions[i];
      StringPattern startSet = _startSets[i];
      if (dependsOnly)
      {
        Set<AtomicVertex> invalids = new HashSet<AtomicVertex>();
        for (AtomicVertex vertex : graph)
        {
          if (startCondition.isFulfilled(vertex))
          {
            for (int j = 0, n = vertex.getNumberOfOutgoingArcs(); j < n; j++)
            {
              Vertex headVertex = vertex.getHeadVertex(j);
              if (_finalCondition.isFulfilled(headVertex) == false
                      && startCondition.isFulfilled(headVertex) == false)
              {
                invalids.add(vertex);
                invalids.add((AtomicVertex) headVertex);
              }
            }
          }
        }
        result.add(new DependencyResult(startSet, _finalSet, toString(startSet, _finalSet),
                invalids.toArray(new AtomicVertex[0])));
      } else
      {
        for (int j = 0; j < _finalConditions.length; j++)
        {
          PathsFinder finder =
                  new PathsFinder(startCondition, _finalConditions[j], _renderer
                          .onlyShortestPaths(), directPathsOnly);
          result.add(new DependencyResult(startSet, _finalSets[j], toString(i, j), finder
                  .findPaths(graph)));
        }
      }
    }
    return result;
  }
  
  private String toString(int i, int j)
  {
    return toString(_startSets[i], _finalSets[j]);
  }

  private String toString(StringPattern startSet, StringPattern finalSet)
  {
    StringBuffer buffer = new StringBuffer(CHECK);
    buffer.append(_repository.toString(startSet)).append(' ')
          .append(_dependencyType).append(' ')
          .append(_repository.toString(finalSet));
    return new String(buffer);
  }
  
  public String toString()
  {
    StringBuffer buffer = new StringBuffer(CHECK);
    for (int i = 0; i < _startSets.length; i++)
    {
      buffer.append(_repository.toString(_startSets[i])).append(' ');
    }
    buffer.append(_dependencyType).append(' ');
    for (int i = 0; i < _finalSets.length; i++)
    {
      buffer.append(_repository.toString(_finalSets[i])).append(' ');
    }

    return new String(buffer.substring(0, buffer.length() - 1));
  }
}
